home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgramD2.iso / Visual Database / Visual Foxpro 6.0 (Ent. Edition) / Vf6ent Extractor.EXE / TOOLS / XSOURCE / XSOURCE.ZIP / vfpsource / wizards / Wzmail / MAILMRGE.PRG next >
Encoding:
Text File  |  1998-05-01  |  38.9 KB  |  1,528 lines

  1. *-
  2. *- foundation class for MailMerge
  3. *-
  4.  
  5. #INCLUDE "mailmrge.h"
  6. #INCLUDE "DDECmd.h"                && localized DDE and WordBasic commands
  7.  
  8. #DEFINE L_DEBUG .F.
  9.  
  10. DEFINE CLASS MailMerge AS CUSTOM
  11.  
  12.     cAppTitle    = ALERTTITLE_LOC    && application name, used in Alerts
  13.     
  14.     iMaxWordProc = N_COMMADELIM        && maximum value for word processors
  15.     
  16.     nWordProc    = N_WORD60            && default is Word 6.0
  17.     nNewDoc        = N_NEW_DOC            && create a new or use an existing document
  18.     cDocName    = ""                && name of master merge document
  19.     nTemplate    = N_FORMLETTER        && type of document to create (Word 6.0 only)
  20.     cMrgData    = ""                && file that will contain merge data if non-60
  21.  
  22.     *- these properties are internal, and not exposed in the UI
  23.     cFieldList    = ""
  24.     cDataFile    = ""                && 2.6 copy of 3.0 DBF table
  25.     cDataSrc    = ""                && datasource
  26.     cODBCSource = ""                && connection string
  27.     cSqlStmt    = ""                && SQL statement for extracting data
  28.     cSqlStmt2    = ""                && SQL statement for extracting data
  29.     cSaveFile    = ""                && file for holding merged data (may not be used in some cases)
  30.     cTmpFile    = ""
  31.     cDataPath    = ""                && path to FROM table
  32.  
  33.     currentOS    = 0                    && operating system
  34.     lHasVerWord = .F.                && verified that Word exists
  35.     cWordVersion = ""                && version of Word installed, as char (e.g., '8')
  36.     lEnglish    = .T.                && Does Word recognize English WordBasic commands?
  37.  
  38.     lAlerted    = .F.                && has user been notified of failure?
  39.  
  40.     cExe        = ""                && full path to the Word application
  41.  
  42.     DIMENSION aODBCDrivers[1,2]        && array of installed drivers
  43.     aODBCDrivers = ""
  44.  
  45.     sysch    = -1                    && DDE channel
  46.  
  47.     DIMENSION aWPMrg[2]                && procedures for handling various merge styles
  48.  
  49.     aWPMrg[1] = "THIS.MrgWord"
  50.     aWPMrg[2] = IIF(_mac,"THIS.MrgCommaDel(CHR(9))","THIS.MrgCommaDel")
  51.  
  52. #IF L_DEBUG
  53.     PROCEDURE Error
  54.         PARAMETER ErrorNum, Method, Line
  55.         THIS.ALERT("Error: " + message() + C_CRLF + ;
  56.             "Error Number: " + ALLT(STR(m.Errornum)) + C_CRLF + ;
  57.             "Method: " + m.Method + C_CRLF + ;
  58.             "Line: " + LTRIM(STR(LINENO())) + ": " + message(1))
  59.         SET TRBE OFF
  60.         ACTI WINDOW DEBUG
  61.         ACTI WINDOW TRACE
  62.         SUSPEND
  63.     ENDPROC
  64. #ENDIF
  65.  
  66.  
  67.     *----------------------------------
  68.     PROCEDURE Init
  69.     *----------------------------------
  70.         IF _mac
  71.             THIS.SetErrorOff = .T.
  72.             THIS.cExe = THIS.LocateApp(C_MSWORDCREATOR)
  73.             IF EMPTY(THIS.cExe)
  74.                 *- assume word is not installed
  75.                 IF "FOXTOOLS" $ SET("LIBRARY")
  76.                     *- assume library loaded and was okay, and Word really isn;t there
  77.                     THIS.Alert(E_NOWORDMACERR_LOC)
  78.                 ENDIF
  79.                 RETURN .F.
  80.             ENDIF
  81.             THIS.SetErrorOff = .F.
  82.         ENDIF
  83.  
  84.         THIS.sysch = -1
  85.         THIS.aODBCDrivers = ""
  86.         THIS.aAutoFields = ""
  87.         
  88.         
  89.         RETURN .T.
  90.  
  91.     ENDPROC
  92.  
  93.     *----------------------------------
  94.     PROCEDURE Merge
  95.     *----------------------------------
  96.     
  97.         LOCAL m.cProc
  98.         
  99.         *- call the procedure that does the work
  100.         
  101.         *- make sure values are reasonable
  102.         IF !BETWEEN(THIS.nWordProc,1,THIS.iMaxWordProc)
  103.             *- invalid word processor index
  104.             THIS.Alert(E_BADWORDPROC_LOC)
  105.             RETURN
  106.         ENDIF
  107.  
  108.         IF !BETWEEN(THIS.nTemplate,1,N_CATALOG)
  109.             *- invalid template index (should be 1 - 4)
  110.             THIS.Alert(E_BADTEMPLATE_LOC)
  111.             RETURN
  112.         ENDIF
  113.  
  114.         IF THIS.nNewDoc == N_EXISTING_DOC AND EMPTY(THIS.cDocName)
  115.             *- no existing document was specified
  116.             THIS.Alert(E_BADEXDOC_LOC)
  117.             RETURN
  118.         ENDIF
  119.          
  120.         IF THIS.nWordProc == N_COMMADELIM AND EMPTY(THIS.cMrgData)
  121.             *- no destination file for merge data specified
  122.             THIS.Alert(E_BADMRGDATA_LOC)
  123.             RETURN
  124.         ENDIF
  125.  
  126.         IF EMPTY(THIS.cAlias)
  127.             *- no alias specified
  128.             THIS.Alert(E_NODATA_LOC)
  129.             RETURN
  130.         ENDIF
  131.         
  132.         IF EMPTY(THIS.aAutoFields[1,1])
  133.             *- no alias specified
  134.             THIS.Alert(E_NOFIELDS_LOC)
  135.             RETURN
  136.         ENDIF
  137.         
  138.         THIS.MakeFieldList(255)
  139.         
  140.         cProc = THIS.aWPMrg[THIS.nWordProc]
  141.         &cProc
  142.         
  143.     ENDPROC
  144.     
  145.     *----------------------------------
  146.     PROCEDURE MrgWord
  147.     *----------------------------------
  148.         *- drive versions of MS Word
  149.         IF !THIS.PrepData()
  150.             RETURN .F.
  151.         ENDIF
  152.     
  153.         IF _mac
  154.             *- we'll use Applescript for doing this on the Mac
  155.             RETURN THIS.MailMergeMacWord6()
  156.         ENDIF
  157.     
  158.         *- get the version of Word that's installed (and while we're at it, the command line)
  159.         THIS.cexe = THIS.GetMSW(C_WORD6_OR_LATER)        && will also fill in THIS.cWordVersion
  160.         
  161.         DO CASE
  162.             CASE THIS.cWordVersion = '8'
  163.                 THIS.MailMergeWinWord6            
  164.                 *- THIS.MailMergeWinWord8
  165.             OTHERWISE
  166.                 THIS.MailMergeWinWord6            
  167.         ENDCASE
  168.         
  169.     ENDPROC
  170.     
  171.     *----------------------------------
  172.     PROCEDURE MakeFieldList
  173.     *----------------------------------
  174.         PARAMETER iMaxFldLen, cDelimit
  175.         
  176.         *- make field list
  177.         THIS.cFieldList = ""
  178.         FOR m.i = 1 TO ALEN(THIS.aAutoFields)
  179.             THIS.cFieldList = THIS.cFieldList + LEFT(THIS.aAutoFields[i],iMaxFldLen) + ','
  180.         NEXT
  181.         THIS.cFieldList = LEFT(    THIS.cFieldList,LEN(THIS.cFieldList) - 1)    && remove extra delimiter
  182.  
  183.     ENDPROC
  184.  
  185.     *----------------------------------
  186.     PROCEDURE PrepData
  187.     *----------------------------------
  188.     
  189.         *- prepare data source
  190.         
  191.         LOCAL cFlds, cOldSafe, iLen, iFH, lCopyFile, cDBQPath
  192.         
  193.         IF THIS.nNewDoc = N_EXISTING_DOC
  194.             THIS.cDocName = ALLT(THIS.cDocName)
  195.             IF EMPTY(THIS.cDocName)
  196.                 RETURN .F.
  197.             ENDIF
  198.         ENDIF
  199.  
  200.         IF !_mac                                                && we aren't using ODBC on Mac
  201.             *- get ODBC drivers -- we'll need this info later on
  202.             *- Check for proper ODBC drivers
  203.             IF !THIS.ODBCCheck()
  204.                 RETURN .F.
  205.             ENDIF
  206.         ENDIF
  207.  
  208.         m.lCopyFile = .F.
  209.  
  210.         IF THIS.lHas30Drivers AND CURSORGETPROP('sourcetype') # K_TABLE
  211.             *- assume is a view that Word 6.0 can't handle -- don't use datasource
  212.             THIS.cDataSrc = ""
  213.         ELSE
  214.             THIS.cDataSrc = DBF()
  215.         ENDIF
  216.  
  217.         *- Check if we have a DBC in use
  218.         SELECT (THIS.cAlias)
  219.  
  220.         *- Check if 3.0 DBC is opened exclusively
  221.         IF THIS.lHas30Drivers AND !EMPTY(THIS.cDBCAlias) AND ISEXCL(THIS.JustStem(THIS.cDBCAlias),2)
  222.             IF THIS.ALERT(C_EXCLDBC1_LOC + THIS.cDBCAlias + C_EXCLDBC2_LOC,36) == "YES"
  223.                 cTmpDbcAlias = THIS.cDBCAlias
  224.                 SET DATABASE TO (m.cTmpDbcAlias)
  225.                 CLOSE DATABASE 
  226.                 OPEN DATABASE (THIS.cDBCName) SHARED
  227.                 USE (THIS.cDBCTable) ALIAS (THIS.cAlias) SHARED
  228.             ELSE
  229.                 RETURN
  230.             ENDIF
  231.         ENDIF
  232.         
  233.         *- Check for 3.0 Table type OR View
  234.         *- Mac doesn't use ODBC so can handle 3.0 files, but can't handle Views
  235.         IF !m.lCopyFile AND ;
  236.             ((!_mac AND !THIS.lHas30Drivers AND;
  237.             (VAL(SYS(2029)) = DBFTYPE_30 OR CURSORGETPROP('sourcetype') # K_TABLE)) OR;
  238.             (_mac AND CURSORGETPROP("sourcetype") # K_TABLE))
  239.             IF THIS.Alert(IIF(_mac,C_COPYFOX3_LOC,C_COPYFOX2_LOC),36) $ "YES"
  240.                 lCopyFile = .T.
  241.             ELSE
  242.                 RETURN .F.
  243.             ENDIF
  244.         ENDIF
  245.  
  246.  
  247.         IF m.lCopyFile        
  248.                     
  249.             LOCAL m.cSaveFile
  250.  
  251.             cFlds = THIS.cFieldList
  252.  
  253.             IF CURSORGETPROP('sourcetype') # 3         && OR ATC(".TMP",DBF())#0
  254.                 THIS.cDataFile = CursorGetprop("sourcename")
  255.             ELSE
  256.                 THIS.cDataFile = THIS.JustStem(DBF())
  257.             ENDIF
  258.  
  259.             iLen = LEN(THIS.cDataFile)
  260.             IF RIGHT(STR(VAL(THIS.cDataFile) + 10^iLen,iLen + 1),iLen) = THIS.cDataFile
  261.                 *- apparently a view? The filename is all numbers
  262.                 *- use the alias instead
  263.                 THIS.cDataFile = LEFT(THIS.cAlias,6)
  264.             ENDIF
  265.             DO CASE
  266.             CASE LEN(THIS.cDataFile) < 7
  267.                 THIS.cDataFile = THIS.cDataFile + "_2"
  268.             CASE LEN(THIS.cDataFile) = 7
  269.                 THIS.cDataFile = THIS.cDataFile + "2"
  270.             CASE RIGHT(THIS.cDataFile,1) = "2"
  271.                 THIS.cDataFile = LEFT(THIS.cDataFile,7) + "_"
  272.             OTHERWISE
  273.                 THIS.cDataFile = LEFT(THIS.cDataFile,7) + "2"
  274.             ENDCASE
  275.                             
  276.             m.coldsafe = SET("SAFE")
  277.             SET SAFETY ON
  278.             THIS.cDataFile = PUTFILE(C_COPYPROMPT_LOC,THIS.cDataFile + ".dbf","DBF")
  279.             SET SAFETY &coldsafe
  280.             IF EMPTY(THIS.cDataFile)
  281.                 RETURN .F.
  282.             ENDIF
  283.  
  284.             THIS.cDataFile = THIS.FORCEEXT(THIS.cDataFile,"DBF")
  285.  
  286.             *- check if file exists, and if so, make sure that we can overwrite it
  287.             IF FILE(THIS.cDataFile)
  288.                 THIS.SetErrorOff = .t.
  289.                 ERASE (THIS.cDataFile)
  290.                 THIS.SetErrorOff = .f.
  291.                 IF FILE(THIS.cDataFile)
  292.                     *- can't get rid of file, so fail
  293.                     THIS.Alert(E_NOREPLACETBL_LOC)
  294.                     RETURN .F.
  295.                 ENDIF
  296.             ENDIF
  297.             
  298.             IF CURSORGETPROP("sourcetype") # K_TABLE
  299.                 =REQUERY()            && get some data
  300.             ENDIF
  301.             COPY FIELDS &cFlds TO (THIS.cDataFile) TYPE FOX2
  302.  
  303.             *- Copied ok?
  304.             IF !FILE(THIS.cDataFile)
  305.                 RETURN .F.
  306.             ENDIF
  307.  
  308.             SELECT 0
  309.             USE (THIS.cDataFile) SHARED
  310.  
  311.             *- Failed somewhere
  312.             IF EMPTY(ALIAS()) OR VAL(SYS(2029))=DBFTYPE_30
  313.                 RETURN .F.
  314.             ELSE
  315.                 *- get the SQL statement now, while we are using this temp table
  316.                 THIS.cDataSrc = THIS.cDataFile
  317.                 *- redo field list, since max for Fox 2.x table is only 10 chars
  318.                 THIS.MakeFieldList(10)
  319.                 THIS.GetSQLSt
  320.             ENDIF
  321.             USE
  322.  
  323.             SELECT (THIS.cAlias)
  324.  
  325.         ELSE
  326.             THIS.cDataFile = DBF()
  327.             THIS.GetSQLSt
  328.         ENDIF
  329.  
  330.         THIS.cSqlStmt = STRTRAN(THIS.cSqlStmt,'"',"")        && Word complains if quotes surround the table name
  331.                                                             && but VFP won't execute SQL statement unless they are there
  332.  
  333.         IF THIS.nWordProc = N_WORD60 AND !_mac
  334.             DO CASE
  335.                 CASE LEN(THIS.csqlstmt) > 510
  336.                     THIS.Alert(C_ERROR_SQL_LOC)
  337.                     THIS.csqlstmt = ""            && error return below
  338.                 CASE LEN(THIS.csqlstmt) > 255
  339.                     THIS.csqlstmt2 = SUBS(THIS.csqlstmt,256)
  340.                     THIS.csqlstmt = LEFT(THIS.csqlstmt,255)
  341.                 OTHERWISE
  342.                     *- do nothing
  343.             ENDCASE
  344.         ENDIF
  345.  
  346.         IF EMPTY(THIS.csqlstmt)
  347.             *- couldn't come up with SQL statement
  348.             RETURN .F.
  349.         ENDIF
  350.  
  351.         *- need to open table as shared access
  352.         IF ISEXCLUSIVE() AND CURSORGETPROP('sourcetype') = K_TABLE
  353.             USE IN (THIS.cAlias)
  354.             m.iFH = FOPEN(THIS.cDataFile,0)
  355.             IF m.iFH == -1
  356.                 *- can't open file, so fail
  357.                 THIS.ALERT(E_NOOPENTBL_LOC)
  358.                 RETURN .F.
  359.             ELSE
  360.                 =FCLOSE(m.iFH)
  361.             ENDIF
  362.             USE (THIS.cDataFile) ALIAS (THIS.cAlias) SHARED
  363.         ENDIF
  364.  
  365.         *- Get data -- should use same directory as foxpro table
  366.         SELECT (THIS.cAlias)
  367.         m.cDBQPath = IIF(EMPTY(THIS.cDataPath),SYS(2027,SET("DEFA") + SYS(2003)),THIS.cDataPath)
  368.  
  369.          IF THIS.lHas30Drivers
  370.             THIS.cODBCSource = "DSN="+THIS.cODBC_DSN+;
  371.                          ";SourceDB="+IIF(EMPTY(THIS.cDBCName),m.cDBQPath,THIS.cDBCName)+;
  372.                          ";SourceType="+IIF(EMPTY(THIS.cDBCName),"DBF","DBC")+;
  373.                          ";Exclusive=No"+;
  374.                          ";BackgroundFetch=Yes;"
  375.         ELSE
  376.             THIS.cODBCSource =    "DSN=" + THIS.cODBC_DSN + ;
  377.                             ";DBQ=" + m.cDBQPath + ;
  378.                             ";DefaultDir=" + m.cDBQPath + ;
  379.                             ";FIL=" + THIS.cODBC_FIL +";"
  380.         ENDIF
  381.  
  382.     ENDPROC
  383.     
  384.     
  385.     *----------------------------------
  386.     PROCEDURE MailMergeWinWord8
  387.     *----------------------------------
  388.         *- drive MS Word 6.0
  389.  
  390.         PRIVATE colddocs, wa
  391.         LOCAL cDummy, nWordDocType, cSqlStmt, cODBCSource, cDBQPath
  392.         LOCAL oDoc
  393.  
  394.         *- We need to set the Localization ID to english (1033)
  395.         *- so that OLE Automation will be understood by OLE server.
  396.         =SYS(3006,I_ENGLISH)
  397.         
  398.         *- create word object
  399.         wa = CreateObject(WIN_8OBJ)
  400.  
  401.         *- Check if problem creating Word object
  402.         IF TYPE('wa') # 'O'
  403.             THIS.ALERT(E_NOOPENWORD_LOC)
  404.             RETURN
  405.         ENDIF
  406.  
  407.         *- Test language
  408.         THIS.lEnglish = (wa.application.international[26] == I_ENGLISH)
  409.  
  410.  
  411.         IF THIS.nNewDoc = N_EXISTING_DOC
  412.             oDoc = wa.documents.open(THIS.cDocName)
  413.         ELSE
  414.             oDoc = wa.documents.new
  415.             *- set main document type
  416.             DO CASE
  417.                 CASE THIS.nTemplate = N_LABEL
  418.                     m.nWordDocType = 1
  419.                 CASE THIS.nTemplate = N_ENVELOPE
  420.                     m.nWordDocType = 2
  421.                 CASE THIS.nTemplate = N_CATALOG
  422.                     m.nWordDocType = 3
  423.                 OTHERWISE
  424.                     m.nWordDocType = 0
  425.             ENDCASE
  426.             oDoc.MailMerge.MainDocumentType = m.nWordDocType
  427.         ENDIF
  428.  
  429.         *- attach data file
  430.         oDoc.MailMerge.OpenDataSource(THIS.cDataSrc,0,0,0,0,0,"","",0,"","",m.cODBCSource,;
  431.             THIS.csqlstmt, THIS.csqlstmt2)
  432.  
  433.         *- activate MSW with proper document
  434.         wa.Visible = .T.
  435.         wa.Activate
  436.         
  437.  
  438.         *- try to display the appropriate dialog 
  439.         IF THIS.nNewDoc = N_NEW_DOC
  440.             *- display Word MailMergeHelper dialog
  441.             wa.Dialogs[I_WDDIALOGMAILMERGEHELPER].Show
  442.         ENDIF
  443.  
  444.         *- terminate the connection
  445.         wa = .NULL.
  446.  
  447.         RETURN
  448.     
  449.     ENDPROC
  450.     
  451.     *----------------------------------
  452.     PROCEDURE MailMergeWinWord6
  453.     *----------------------------------
  454.         *- drive MS Word 6.0
  455.  
  456.         PRIVATE colddocs, wa
  457.         LOCAL cDummy, nWordDocType, cSqlStmt, cODBCSource, cDBQPath
  458.  
  459.         *- launch MSW, so it hangs around after word.basic is done
  460.         IF !THIS.StartWord(C_WORD6)
  461.             RETURN .F.
  462.         ELSE
  463.             *- We need to set the Localization ID to english (1033)
  464.             *- so that OLE Automation will be understood by OLE server.
  465.             =SYS(3006,I_ENGLISH)
  466.         ENDIF
  467.         
  468.         *- create word object
  469.         wa = CreateObject(WIN_6OBJ)
  470.  
  471.         *- Check if problem creating Word object
  472.         IF TYPE('wa') # 'O'
  473.             THIS.ALERT(E_NOOPENWORD_LOC)
  474.             RETURN
  475.         ENDIF
  476.  
  477.         *- Test language
  478.         THIS.lEnglish = .T.
  479.         THIS.SetErrorOff = .t.
  480.         m.cdummy = wa.AppInfo(16)
  481.         IF THIS.HadError
  482.             THIS.lEnglish = .f.
  483.             THIS.HadError = .f.
  484.         ENDIF
  485.         THIS.SetErrorOff = .f.
  486.  
  487.         *- Get data -- should use same directory as foxpro table
  488.         SELECT (THIS.cAlias)
  489.         m.cDBQPath = IIF(EMPTY(THIS.cDataPath),SYS(2027,SET("DEFA") + SYS(2003)),THIS.cDataPath)
  490.  
  491.          IF THIS.lHas30Drivers
  492.             cODBCSource = "DSN="+THIS.cODBC_DSN+;
  493.                          ";SourceDB="+IIF(EMPTY(THIS.cDBCName),m.cDBQPath,THIS.cDBCName)+;
  494.                          ";SourceType="+IIF(EMPTY(THIS.cDBCName),"DBF","DBC")+;
  495.                          ";Exclusive=No"+;
  496.                          ";BackgroundFetch=Yes;"
  497.         ELSE
  498.             cODBCSource =    "DSN=" + THIS.cODBC_DSN + ;
  499.                             ";DBQ=" + m.cDBQPath + ;
  500.                             ";DefaultDir=" + m.cDBQPath + ;
  501.                             ";FIL=" + THIS.cODBC_FIL +";"
  502.         ENDIF
  503.  
  504.         IF THIS.lEnglish
  505.             IF THIS.nNewDoc = N_EXISTING_DOC
  506.                 wa.ENG_OLE_FILEOPEN(THIS.cDocName)
  507.             ELSE
  508.                 wa.ENG_OLE_FILENEW
  509.                 *- set main document type
  510.                 DO CASE
  511.                     CASE THIS.nTemplate = N_LABEL
  512.                         m.nWordDocType = 1
  513.                     CASE THIS.nTemplate = N_ENVELOPE
  514.                         m.nWordDocType = 2
  515.                     CASE THIS.nTemplate = N_CATALOG
  516.                         m.nWordDocType = 3
  517.                     OTHERWISE
  518.                         m.nWordDocType = 0
  519.                 ENDCASE
  520.                 IF THIS.lEnglish
  521.                     wa.ENG_OLE_MMERGEDOCTYPE(m.nWordDocType)
  522.                 ELSE
  523.                     wa.X_OLE_MMERGEDOCTYPE_LOC(m.nWordDocType)
  524.                 ENDIF
  525.             ENDIF
  526.  
  527.             *- attach data file
  528.             wa.ENG_OLE_MMERGEOPENSRC(THIS.cDataSrc,0,0,0,0,"","",0,"","",m.cODBCSource,;
  529.                 THIS.csqlstmt, THIS.csqlstmt2)
  530.  
  531.             *- activate MSW with proper document
  532.             wa.ENG_OLE_APPRESTORE                && this doesn't seem to work, hence... 
  533.  
  534.             THIS.sysch = DDEInitiate(C_MSWORDWIN,"System")
  535.             IF THIS.sysch > 0
  536.                 =DDEExecute(THIS.sysch,ENG_APPRESTORE)
  537.                 =DDETerminate(THIS.sysch)
  538.             ENDIF
  539.  
  540.             wa.ENG_OLE_APPACTIVATE(WIN_SECT6,1)
  541.  
  542.         ELSE
  543.             IF THIS.nNewDoc = N_EXISTING_DOC
  544.                 wa.X_OLE_FILEOPEN_LOC(THIS.cDocName)
  545.             ELSE
  546.                 wa.X_OLE_FILENEW_LOC
  547.             ENDIF
  548.  
  549.             *- attach data file
  550.             wa.X_OLE_MMERGEOPENSRC_LOC(THIS.cDataSrc,0,0,0,0,"","",0,"","",m.cODBCSource,;
  551.                 THIS.csqlstmt, THIS.csqlstmt2)
  552.  
  553.             *- activate MSW with proper document
  554.             wa.X_OLE_APPRESTORE_LOC
  555.  
  556.             THIS.sysch = DDEInitiate(C_MSWORDWIN,"System")
  557.             IF THIS.sysch > 0
  558.                 =DDEExecute(THIS.sysch,X_APPRESTORE_LOC)
  559.                 =DDETerminate(THIS.sysch)
  560.             ENDIF
  561.  
  562.             wa.X_OLE_APPACTIVATE_LOC(WIN_SECT6,1)
  563.         ENDIF
  564.  
  565.         *- try to display the appropriate dialog 
  566.         IF THIS.nNewDoc = N_NEW_DOC
  567.             *- display appropriate Word dialog
  568.             DO CASE
  569.                 CASE THIS.nTemplate = N_LABEL
  570.                     THIS.mswmldlg(N_LABEL)
  571.                 CASE THIS.nTemplate = N_ENVELOPE
  572.                     THIS.mswmldlg(N_ENVELOPE)
  573.                 CASE THIS.nTemplate = N_CATALOG
  574.                     THIS.mswmldlg(N_CATALOG)
  575.                 OTHERWISE
  576.             ENDCASE
  577.         ENDIF
  578.  
  579.         *- terminate the connection
  580.         wa = .NULL.
  581.  
  582.         RETURN
  583.     ENDFUNC
  584.  
  585.     *----------------------------------
  586.     FUNCTION  mswmldlg
  587.     *----------------------------------
  588.         PARAMETER itmpl
  589.  
  590.  
  591.         IF THIS.nWordProc # N_WORD60
  592.             RETURN .T.
  593.         ENDIF
  594.  
  595.         IF THIS.lEnglish
  596.             wa.ENG_OLE_TOOLSMACRO("MailMergeHelper",1,0)
  597.         ELSE
  598.             wa.X_OLE_TOOLSMACRO_LOC(X_OLE_MAILMERGEHELPER_LOC,1,0)
  599.         ENDIF
  600.  
  601.         RETURN .T.
  602.  
  603.     ENDFUNC
  604.  
  605.     *----------------------------------
  606.     FUNCTION StartWord
  607.     *----------------------------------
  608.         PARAMETER cversion
  609.         *- Routine is called to check if Word is 
  610.         *- running and/or DDE system channel is set.
  611.         *- Also check if Word was closed by user
  612.         *- while FoxPro screen open.
  613.  
  614.         LOCAL llauncherr, lsafety, cworddir, claunchmsg, m.nLastDDEError, cSysItems,;
  615.             cScript, cExe
  616.  
  617.         m.llauncherr = .F.
  618.  
  619.         DO CASE
  620.             CASE _windows
  621.                 THIS.cexe = C_MSWORDEXE
  622.             CASE _mac
  623.                 THIS.cexe = C_MSWORDMAC
  624.             OTHERWISE
  625.                 THIS.cexe = ""
  626.         ENDCASE
  627.  
  628.         DO CASE
  629.             CASE cversion = C_WORD6
  630.                 m.cLaunchMsg = C_STARTWORD60_LOC
  631.             OTHERWISE
  632.                 RETURN .F.
  633.         ENDCASE
  634.             
  635.         m.cOldErr = ON('ERROR')
  636.         ON ERROR m.llauncherr = .T.
  637.  
  638.         IF _mac
  639.             *-
  640.             *- note -- the following is to support interacting with Word via OLE automation 
  641.             *- It is not currently used (Applescript is used instead, called above)
  642.             *-
  643.             *- get word location
  644.             *- force user to locate it
  645.  
  646.             THIS.cexe = "Raw:Apps:Microsoft Office:Microsoft Word 6:Microsoft Word"
  647.             IF !FILE(THIS.cexe)
  648.                 THIS.cexe = SYS(2027,GETFILE("","Locate " + ALLT(THIS.cexe),"",0,'APPL'))
  649.             ENDIF
  650.             IF EMPTY(THIS.cexe)
  651.                 *- cancelled
  652.                 RETURN .F.
  653.             ELSE
  654.                 *- update prefs
  655.                 *=PutPref('PREFM',K_WORD6ID,'MS Word 6.0 Location',.F.,C_MSWORDMAC)
  656.             ENDIF
  657.  
  658.             *- create Applescript
  659.             cScript = SYS(2027,SYS(2023)) + SYS(3) + ".script"
  660.  
  661.             SET TEXTMERGE TO (m.cScript)
  662.             SET TEXTMERGE ON NOSHOW
  663.             \\ -- AppleScript¬ script to launch MS Word for Macintosh 6.0
  664.             \tell application "<<THIS.cexe>>"
  665.             \    Run
  666.             \end tell
  667.             *- close textmerge file, strip out linefeeds
  668.             SET TEXTMERGE TO
  669.             THIS.FxStripLF(m.cScript)
  670.             *- run Applescript
  671.             RUNSCRIPT (m.cScript)
  672.  
  673.         ELSE
  674.             *- Not Macintosh
  675.             *- Terminate a prior channel if user quit Word
  676.             *- while still in screen.
  677.             IF THIS.sysch # -1
  678.                 =DDERequest(THIS.sysch,'topics')
  679.                 m.nLastDDEError = DDELastError()
  680.                 IF m.nLastDDEError == N_NOCLIENTERR OR m.nLastDDEError == N_BADCHANNELERR 
  681.                     =DDETerminate(THIS.sysch)
  682.                     THIS.sysch = -1
  683.                 ENDIF
  684.             ENDIF
  685.             IF THIS.sysch = -1
  686.                 m.lsafety = DDESetOption("SAFETY")
  687.                 =DDESetOption("SAFETY",.F.)
  688.                 THIS.sysch = DDEInitiate(C_MSWORDWIN,"System")
  689.                 IF THIS.sysch = -1                && failed
  690.                     *- obtain Word directory
  691.                     THIS.cexe = THIS.GetMSW(m.cversion)
  692.                     IF EMPTY(THIS.cexe)
  693.                         *- failed to find .INI info, so fail
  694.                         THIS.sysch = -1
  695.                     ELSE
  696.                         WAIT WINDOW m.cLaunchMsg NOWAIT
  697.                         m.cexe = THIS.cexe
  698.                         RUN /N7 &cexe
  699.                         THIS.sysch = DDEInitiate(C_MSWORDWIN,"System")
  700.                     ENDIF
  701.                 ENDIF
  702.                 IF THIS.sysch = -1 AND !THIS.lAlerted
  703.                     THIS.ALERT(E_NOMSWLAUNCH_LOC)
  704.                 ENDIF
  705.             ENDIF
  706.             IF THIS.sysch <> -1
  707.                 *- if they got it launched
  708.                 *- check version of word
  709.                 IF !THIS.checkver(m.cversion)
  710.                     THIS.ALERT(E_WRONGWORD_LOC)
  711.                     =DDETerminate(THIS.sysch)
  712.                     THIS.sysch = -1
  713.                 ENDIF
  714.             ENDIF
  715.         ENDIF
  716.  
  717.         *- okay, re-set error handler
  718.         ON ERROR &cOldErr
  719.         IF _mac
  720.             THIS.sysch = 0        && so it looks like success
  721.         ELSE
  722.             =DDESetOption("SAFETY",m.lsafety)
  723.         ENDIF
  724.         WAIT CLEAR
  725.  
  726.         RETURN (THIS.sysch # -1)
  727.  
  728.     ENDPROC    && startword
  729.  
  730.  
  731.     *----------------------------------
  732.     FUNCTION checkver
  733.     *----------------------------------
  734.  
  735.         *- Verify WORD version
  736.  
  737.         PARAMETER m.cversion
  738.  
  739.         LOCAL lwoozle
  740.  
  741.         m.lwoozle = ("Woozle" $ DDERequest(THIS.sysch,'Formats'))
  742.         RETURN ((m.cversion = "2.0" AND !m.lwoozle) OR (m.cversion = "6.0" AND m.lwoozle))
  743.     ENDFUNC
  744.  
  745.     *----------------------------------
  746.     FUNCTION GetMSW
  747.     *----------------------------------
  748.         *- get the MS Word command line from registry file
  749.  
  750.         PARAMETER m.cversion
  751.  
  752.         LOCAL cCommand, cExtKey, oReg
  753.  
  754.         cCommand = ""
  755.         cExtKey = ""
  756.  
  757.         IF !("REGISTRY" $ SET("PROC"))
  758.             SET PROCEDURE TO registry ADDITIVE
  759.         ENDIF
  760.         
  761.         oReg = CREATE("registry")
  762.         IF TYPE("oReg") # 'O'
  763.             *- failed to create registry object, so fail...
  764.             RETURN ""
  765.         ENDIF
  766.  
  767.         DO CASE
  768.         
  769.             CASE m.cversion = C_WORD6_OR_LATER
  770.  
  771.                 *- look in the Registry for the current installed version of Word
  772.                 IF (oReg.GetLatestVersion(REG_MSWDOC_KEY,@cExtKey,@cCommand) != ERROR_SUCCESS)
  773.                     RETURN ""
  774.                 ENDIF
  775.                 
  776.                 THIS.cWordVersion = RIGHT(cExtKey,1)
  777.                 IF VAL(THIS.cWordVersion) < VAL(C_WORD6)
  778.                     *- no Word 6.0 or later in Registry
  779.                     RETURN ""
  780.                 ENDIF
  781.                 
  782.                 *- strip off any command line options (e.g., "/W")
  783.                 m.cCommand = ALLT(IIF("/" $ m.cCommand,LEFT(m.cCommand,AT("/",m.cCommand) - 1),m.cCommand))
  784.  
  785.                 *-------------------------------------------------
  786.                 *- if MS Word 6.0 or later, make sure ODBC stuff is present
  787.  
  788.                 IF !THIS.IsODBC(FOXODBC_ANY,"D")
  789.                     WAIT CLEAR
  790.                     THIS.Alert(E_ODBC2_LOC)
  791.                     THIS.lAlerted = .T.
  792.                     RETURN ""
  793.                 ENDIF
  794.  
  795.                 *- set the data source
  796.                 THIS.GetDSN
  797.                 IF EMPTY(THIS.aODBCDrivers[1,1])
  798.                     RETURN ""
  799.                 ENDIF
  800.  
  801.             OTHERWISE
  802.                 m.nRetLen = 0
  803.         ENDCASE
  804.         
  805.         IF EMPTY(m.cCommand)
  806.             *- couldn't get the info
  807.             WAIT CLEAR
  808.             THIS.Alert(STRTRAN(E_NOWORDERR_LOC,"@1",m.cversion))
  809.             THIS.lAlerted = .T.
  810.         ENDIF
  811.  
  812.         RETURN m.cCommand
  813.  
  814.     ENDFUNC
  815.  
  816.     *----------------------------------
  817.     FUNCTION GetSQLSt
  818.     *----------------------------------
  819.         *- get the SQL statement for extracting data
  820.  
  821.         PRIVATE m.nfh, m.cext, m.ccurtable
  822.  
  823.         STORE "" TO csqlstmt, cconnstmt
  824.         m.cfname = ""
  825.  
  826.         IF CURSORGETPROP("sourcetype") = K_TABLE
  827.             *- simple table, so give them the whole mess
  828.             THIS.csqlstmt = [select ] + THIS.cFieldList + [ from '] + THIS.JustFName(SYS(2027,THIS.cDataSrc)) + [']
  829.             THIS.cDataPath = THIS.AddBS(THIS.JustPath(SYS(2027,THIS.cDataSrc)))
  830.         ELSE
  831.             THIS.csqlstmt = DBGETPROP(THIS.cDBCTable,"VIEW","SQL")
  832.         ENDIF
  833.         RETURN
  834.     ENDFUNC
  835.  
  836.  
  837.     *----------------------------------
  838.     PROCEDURE MrgCommaDel
  839.     *----------------------------------
  840.     *-Generate a delimted text file of data
  841.         PARAMETER cdelimit
  842.  
  843.         IF PARAMETER() = 0
  844.             cdelimit = ","
  845.         ENDIF
  846.  
  847.         PRIVATE ncurselect, lretval
  848.  
  849.         m.ncurselect = SELECT()
  850.  
  851.         THIS.cDataSrc = DBF()
  852.  
  853.         THIS.GetSQLSt
  854.         IF EMPTY(THIS.csqlstmt)
  855.             *- couldn't come up with SQL statement
  856.             RETURN .F.
  857.         ENDIF
  858.  
  859.         THIS.cSaveFile = ALLT(PUTFILE(C_MMSAVEAS_LOC,C_DFLTNAME_LOC,EXT_TXT))
  860.         IF EMPTY(THIS.cSaveFile)
  861.             RETURN .F.
  862.         ENDIF
  863.  
  864.         THIS.SaveSql(L_DONTGETFILE)
  865.         IF !EMPTY(THIS.ctmpfile) AND FILE(THIS.ctmpfile)
  866.             SELECT 0
  867.             USE (THIS.ctmpfile)
  868.         ELSE
  869.             RETURN .F.
  870.         ENDIF
  871.     
  872.  
  873.         m.lretval = THIS.wzmmdata(THIS.cSaveFile,m.cdelimit)
  874.  
  875.         IF !EMPTY(THIS.cTmpFile)
  876.             *- close temp file
  877.             USE
  878.             ERASE (THIS.cTmpFile)
  879.             IF FILE(THIS.JustStem(THIS.ctmpfile) + ".FPT")
  880.                 ERASE (THIS.JustStem(THIS.ctmpfile) + ".FPT")
  881.             ENDIF
  882.             IF FILE(THIS.JustStem(THIS.ctmpfile) + ".CDX")
  883.                 ERASE (THIS.JustStem(THIS.ctmpfile) + ".CDX")
  884.             ENDIF
  885.         ENDIF
  886.  
  887.         *- reselect current work area
  888.         SELECT (ncurselect)
  889.  
  890.         RETURN m.lretval
  891.  
  892.     ENDFUNC    && MrgCommaDel
  893.  
  894.  
  895.     *----------------------------------
  896.     FUNCTION wzmmdata
  897.     *----------------------------------
  898.         *-    Copy data to a text file, with field names on line 1
  899.         *-    Assumes source file is open in current work area
  900.         *-
  901.         *-    Parameters    csavefile    C    name of text file to hold results
  902.         *-                cdelimit        C    delimter
  903.         *-
  904.         *-    Returns .T. if success, otherwise .F.
  905.  
  906.         PARAMETER csavefile, cdelimit
  907.  
  908.         PRIVATE wzmmflds, nctr, nfh, ntempfh, nfsize, cbytesread, wztempfile
  909.         LOCAL ccrlf
  910.  
  911.         IF _mac
  912.             ccrlf = IIF(THIS.nWordProc = N_WORD60,CHR(13) + CHR(10), CHR(13))    && so it matches the way Fox exports the data
  913.         ELSE
  914.             ccrlf = CHR(13) + CHR(10)
  915.         ENDIF
  916.  
  917.  
  918.         STORE -1 TO m.nfh, m.ntempfh
  919.         m.wztempfile = ""
  920.  
  921.         *- step 1: create the destination file
  922.         m.nfh = FCREATE(m.csavefile,0)
  923.         IF m.nfh = -1
  924.             *- failed to create file
  925.             THIS.MMCleanup(.F.,m.nfh, m.ntempfh, m.wztempfile)
  926.             RETURN .F.
  927.         ENDIF
  928.  
  929.         *- step 2 -- write out header (field names)
  930.         m.nfcount = AFIELDS(wzmmflds)
  931.         FOR m.nctr = 1 TO m.nfcount
  932.             *- keep last field name even if memo, to prevent invalid field name err
  933.             IF wzmmflds[m.nctr,2] $ 'MG' AND m.nctr < m.nfcount
  934.                 *- skip Memo fields and General fields
  935.                 LOOP
  936.             ENDIF
  937.             IF FWRITE(m.nfh,wzmmflds[m.nctr,1]) = 0
  938.                 THIS.MMCleanup(.F.,m.nfh, m.ntempfh, m.wztempfile)
  939.                 RETURN .F.
  940.             ENDIF
  941.             IF m.nctr < m.nfcount
  942.                 IF FWRITE(m.nfh,cdelimit) = 0
  943.                     THIS.MMCleanup(.F.,m.nfh, m.ntempfh, m.wztempfile)
  944.                     RETURN .F.
  945.                 ENDIF
  946.             ENDIF    
  947.         NEXT
  948.  
  949.         IF m.nfcount = 1 AND THIS.nWordProc = N_WORD60 AND _mac
  950.             *- Word complains if there is only one field
  951.             IF FWRITE(m.nfh,cdelimit) = 0
  952.             THIS.MMCleanup(.F.,m.nfh, m.ntempfh, m.wztempfile)
  953.             RETURN .F.
  954.         ENDIF
  955.         ENDIF
  956.  
  957.         IF FWRITE(m.nfh,ccrlf) = 0
  958.             THIS.MMCleanup(.F.,m.nfh, m.ntempfh, m.wztempfile)
  959.             RETURN .F.
  960.         ENDIF
  961.  
  962.         *- step 3 -- write data out to a separate file
  963.         m.wztempfile = SYS(3) + ".TXT"
  964.  
  965.         IF m.cdelimit = K_TAB
  966.             COPY TO (m.wztempfile) DELI WITH TAB
  967.         ELSE
  968.             COPY TO (m.wztempfile) DELI
  969.         ENDIF
  970.  
  971.         *- step 4: move temp file data to end of destination file
  972.         m.ntempfh = FOPEN(m.wztempfile,0)
  973.         IF m.ntempfh = -1
  974.             *- failed to open temp file
  975.             THIS.MMCleanup(.F.,m.nfh, m.ntempfh, m.wztempfile)
  976.             RETURN .F.
  977.         ENDIF
  978.  
  979.         *- get file size, and return pointer to top of file
  980.         =FSEEK(m.ntempfh,0)
  981.  
  982.         *- Readin' and writin'
  983.         DO WHILE !FEOF(m.ntempfh)
  984.             m.cbytesread = FREAD(m.ntempfh,K_TRANSFER)
  985.             IF m.nfcount = 1 AND THIS.nWordProc = N_WORD60 AND _mac
  986.                 *- add a extra delimiter
  987.                 m.cbytesread = STRTRAN(m.cbytesread,ccrlf,cdelimit + ccrlf)
  988.             ENDIF
  989.             IF THIS.nWordProc # N_WORD60 AND _mac
  990.                 *- remove LFs on Mac
  991.                 m.cbytesread = STRTRAN(m.cbytesread,CHR(10),'')
  992.             ENDIF
  993.             m.nbyteswrit = FWRITE(m.nfh, m.cbytesread)
  994.             IF m.nbyteswrit <> LEN(m.cbytesread)
  995.                 *- error writing file
  996.                 THIS.MMCleanup(.F.,m.nfh, m.ntempfh, m.wztempfile)
  997.                 RETURN .F.
  998.             ENDIF
  999.         ENDDO
  1000.  
  1001.         *- that should be it
  1002.         THIS.MMCleanup(.T.,m.nfh, m.ntempfh, m.wztempfile)
  1003.  
  1004.         RETURN .T.
  1005.     ENDFUNC
  1006.  
  1007.     *----------------------------------
  1008.     FUNCTION MMCleanup
  1009.     *----------------------------------
  1010.         *-     Close files, erase temp file
  1011.         PARAMETER lok, nfh1, nfh2, ctempfile
  1012.  
  1013.         IF !m.lok
  1014.             THIS.ALERT(E_FILEERR_LOC)
  1015.         ENDIF
  1016.  
  1017.         IF m.nfh1 > -1
  1018.             =FCLOSE(m.nfh1)
  1019.         ENDIF
  1020.  
  1021.         IF m.nfh2 > -1
  1022.             =FCLOSE(m.nfh2)
  1023.         ENDIF
  1024.  
  1025.         IF !EMPTY(ctempfile) AND FILE(ctempfile)
  1026.             ERASE (ctempfile)
  1027.         ENDIF
  1028.  
  1029.         RETURN  .T.
  1030.     ENDFUNC
  1031.  
  1032.     *----------------------------------
  1033.     FUNCTION SaveSql
  1034.     *----------------------------------
  1035.     *- Generate a table of the SQL query results
  1036.  
  1037.         PARAMETER lgetfname
  1038.  
  1039.         PRIVATE m.ccurtable
  1040.         
  1041.         LOCAL cOldDefa
  1042.  
  1043.         IF lgetfname
  1044.                 m.coldsafe = SET("SAFE")
  1045.                 SET SAFETY ON
  1046.                 THIS.ctmpfile = PUTFILE(C_MMSAVEAS_LOC,C_DFLTDBF_LOC,EXT_DBF)
  1047.                 SET SAFETY &coldsafe
  1048.         ELSE
  1049.             THIS.ctmpfile = SYS(3) + ".DBF"
  1050.         ENDIF
  1051.  
  1052.         IF EMPTY(THIS.ctmpfile)
  1053.             RETURN .F.
  1054.         ELSE
  1055.             m.ccurtable = IIF(!EMPTY(ALIAS()),ALIAS(),"")
  1056.             m.csqlstmt = THIS.csqlstmt
  1057.  
  1058.             *- execute the SQL query
  1059.             cOldDefa = SET("DEFAULT") + CURDIR()
  1060.             IF !EMPTY(THIS.cDataPath)
  1061.                 SET DEFAULT TO (THIS.cDataPath)
  1062.             ENDIF
  1063.             &csqlstmt INTO DBF (THIS.cTmpFile)
  1064.             THIS.ctmpfile = FULLPATH(THIS.ctmpfile)
  1065.             USE
  1066.             IF !EMPTY(m.ccurtable) AND USED(m.ccurtable)
  1067.                 SELECT (ccurtable)
  1068.             ENDIF
  1069.             THIS.csqlstmt = "SELECT * FROM " + THIS.JustFName(THIS.cTmpFile)
  1070.             THIS.cDataPath = THIS.AddBS(THIS.JustPath(THIS.cTmpFile))
  1071.             SET DEFAULT TO (m.cOldDefa)    
  1072.         ENDIF
  1073.         RETURN .T.
  1074.  
  1075.     ENDFUNC    && SaveSql
  1076.  
  1077.     
  1078.     *----------------------------------
  1079.     PROCEDURE IsODBC
  1080.     *----------------------------------
  1081.         PARAMETER cODBCStr, cItemType
  1082.         *- check aODBCDrivers array for presence of requested driver
  1083.         *- cItemType = "D" search for driver (column 2)
  1084.         *-           = "S" search for data source (column 1)
  1085.  
  1086.         LOCAL cOldExact, nVal
  1087.  
  1088.         m.cOldExact = SET("EXACT")
  1089.         SET EXACT OFF
  1090.         nVal = THIS.AScanAny("THIS.aODBCDrivers",cODBCStr,IIF(cItemType = "S",1,2))
  1091.         SET EXACT &cOldExact
  1092.  
  1093.         RETURN (nVal > 0)
  1094.  
  1095.     ENDPROC
  1096.  
  1097.     *----------------------------------
  1098.     PROCEDURE GetDSN
  1099.     *----------------------------------
  1100.         *- set value of DSN
  1101.         *- do cascading list of drivers
  1102.         LOCAL cOldExact, nVal
  1103.  
  1104.         m.cOldExact = SET("EXACT")
  1105.         SET EXACT OFF
  1106.  
  1107.         nVal = THIS.AScanAny("THIS.aODBCDrivers",FOXODBC_30,2)
  1108.         IF nVal > 0
  1109.             THIS.cODBC_DSN = THIS.aODBCDrivers[nVal,1]
  1110.         ELSE        
  1111.             nVal = THIS.AScanAny("THIS.aODBCDrivers",FOXODBC_26,2)
  1112.             IF nVal > 0
  1113.                 THIS.cODBC_DSN = THIS.aODBCDrivers[nVal,1]
  1114.             ELSE
  1115.                 nVal = THIS.AScanAny("THIS.aODBCDrivers",FOXODBC_25,2)
  1116.                 IF nVal > 0
  1117.                     THIS.cODBC_DSN = THIS.aODBCDrivers[nVal,1]
  1118.                 ENDIF
  1119.             ENDIF
  1120.         ENDIF
  1121.  
  1122.         SET EXACT &cOldExact
  1123.  
  1124.     ENDPROC        &&    GetDSN
  1125.  
  1126.     *----------------------------------
  1127.     PROCEDURE GetODBC
  1128.     *----------------------------------
  1129.         *- get a list of the ODBC drivers, and store in a property of the merge engine
  1130.         PARAMETER cSect
  1131.         LOCAL aODBCSects,retval,oINI,cValue,nArrLen,i
  1132.         LOCAL nPos,cSaveExact, retval, cValue
  1133.  
  1134.         DIMENSION aODBCSects[1]
  1135.  
  1136.         IF PARAMETERS()=0
  1137.             cSect = 0
  1138.         ENDIF
  1139.  
  1140.          IF _mac
  1141.             *- look in ODBC preferences file
  1142.             *- get location of System Folder:Preferences
  1143.             *-
  1144.             *- NOTE: we check only the ODBC Settings file (NOT ODBC Settings PPC),
  1145.             *- since the Foxpro Driver is 68K only
  1146.             *-
  1147.             retval = THIS.GetINISection(@aODBCSects,ODBC_SOURCE,ODBC_FILE_68K)
  1148.             DO CASE
  1149.                 CASE m.retval = ERROR_NOINIFILE
  1150.                     THIS.Alert(E_ODBC1_LOC)
  1151.                     RETURN .F.
  1152.                 CASE m.retval = ERROR_NOINIENTRY
  1153.                     *- do nothing
  1154.                 CASE m.retval = ERROR_FAILINI
  1155.                     *- do nothing
  1156.                 OTHERWISE
  1157.                     FOR i = 1 TO ALEN(aODBCSects)
  1158.                         cValue = ""
  1159.                         IF _mac
  1160.                             cValue = THIS.GetPref(ODBC_SOURCE,aODBCSects[m.i],ODBC_FILE_68K)
  1161.                         ENDIF
  1162.                         IF ATC(FOXODBC_ANY,cValue) # 0
  1163.                             IF !EMPTY(THIS.aODBCDrivers[1])
  1164.                                 DIMENSION THIS.aODBCDrivers[ALEN(THIS.aODBCDrivers,1)+1,2]
  1165.                             ENDIF
  1166.                             THIS.aODBCDrivers[ALEN(THIS.aODBCDrivers,1),1] = aODBCSects[m.i]
  1167.                             THIS.aODBCDrivers[ALEN(THIS.aODBCDrivers,1),2] = m.cValue            
  1168.                         ENDIF
  1169.                     ENDFOR
  1170.             ENDCASE
  1171.  
  1172.             IF EMPTY(THIS.aODBCDrivers[1])
  1173.                 *- failed to find any ODBC sources
  1174.                 THIS.ALERT(E_ODBC2_LOC)
  1175.                 THIS.aODBCDrivers = ""
  1176.             ENDIF
  1177.  
  1178.             *- need to check for 3.0 drivers?
  1179.  
  1180.         ELSE
  1181.             retval = oReg.GetINISection(@aODBCSects,m.cSect,ODBC_FILE)
  1182.             DO CASE
  1183.                 CASE m.retval = ERROR_NOINIFILE
  1184.                     THIS.Alert(E_ODBCDLL_LOC)
  1185.                     RETURN .F.
  1186.                 CASE m.retval = ERROR_NOINIENTRY
  1187.                     *- do nothing
  1188.                 CASE m.retval = ERROR_FAILINI
  1189.                     *- do nothing
  1190.                 OTHERWISE
  1191.                     FOR i = 1 TO ALEN(aODBCSects)
  1192.                         cValue = ""
  1193.                         =oReg.GetINIEntry(@cValue,m.cSect,aODBCSects[m.i],ODBC_FILE)
  1194.                         IF ATC("FoxPro",cValue) # 0
  1195.                             IF !EMPTY(THIS.aODBCDrivers[1])
  1196.                                 DIMENSION THIS.aODBCDrivers[ALEN(THIS.aODBCDrivers,1) + 1, 2]
  1197.                             ENDIF
  1198.                             THIS.aODBCDrivers[ALEN(THIS.aODBCDrivers, 1),1] = aODBCSects[m.i]
  1199.                             THIS.aODBCDrivers[ALEN(THIS.aODBCDrivers, 1),2] = m.cValue            
  1200.                         ENDIF
  1201.                     ENDFOR
  1202.             ENDCASE
  1203.  
  1204.             RETURN .T.
  1205.  
  1206.         ENDIF        && _mac
  1207.  
  1208.     ENDPROC
  1209.  
  1210.     *----------------------------------
  1211.     PROCEDURE ODBCCheck
  1212.     *----------------------------------
  1213.  
  1214.         * Checks and gets FoxPro ODBC data sources for MS Query to use
  1215.         * when called by Excel thru OLE automation. Note: MS Query 1.0
  1216.         * reads from INI files and NOT the Registry.
  1217.         
  1218.         LOCAL cBuffer,nBufSize,cDLLName,i,cSection,cValue,aODBCSects
  1219.         cValue = ""
  1220.  
  1221.         SET PROCEDURE TO registry ADDITIVE
  1222.         oReg = CREATE('registry')
  1223.         
  1224.         *- Check to see if we have a registered FoxPro ODBC Data Source in ODBC.INI.
  1225.         IF !THIS.GetODBC(ODBC_SOURCE)
  1226.             THIS.ALERT(E_ODBC1_LOC)
  1227.             RETURN .F.
  1228.         ENDIF
  1229.         
  1230.         *- Check to see if we have a registered FoxPro ODBC 32 bit Data Source in ODBC.INI.
  1231.         IF !THIS.GetODBC(ODBC_32SOURCE)
  1232.             THIS.ALERT(E_ODBC1_LOC)
  1233.             RETURN .F.
  1234.         ENDIF
  1235.         
  1236.         *- Finally, check if "FoxPro Files" is a section but somehow 
  1237.         *- was not listed as a registered ODBC Data Source. Let's
  1238.         *- by default set it to FoxPro 2.6 driver.
  1239.         IF EMPTY(THIS.aODBCDrivers[1])
  1240.             DIMENSION aODBCSects[1]
  1241.             IF oReg.GetINISection(@aODBCSects,THIS.cODBC_DSN,ODBC_FILE) = ERROR_SUCCESS
  1242.                 m.cSection = THIS.cODBC_DSN
  1243.                 THIS.aODBCDrivers[1,1] = THIS.cODBC_DSN
  1244.                 THIS.aODBCDrivers[1,2] = FOXODBC_26FIX
  1245.             ENDIF
  1246.         ENDIF
  1247.         
  1248.         *- Failed to find a FoxPro section
  1249.         IF EMPTY(THIS.aODBCDrivers[1])
  1250.             *- failed to find any ODBC sources
  1251.             THIS.ALERT(E_ODBC2_LOC)
  1252.             RETURN .F.
  1253.         ENDIF
  1254.  
  1255.         *- Get Data Driver name and File type here
  1256.         THIS.GetODBCData()
  1257.         
  1258.         *- Check for correct driver installed
  1259.         THIS.CheckODBCFoxVer()
  1260.  
  1261.         RETURN .T.
  1262.     ENDPROC
  1263.  
  1264.  
  1265.     *----------------------------------
  1266.     PROCEDURE GetODBCData
  1267.     *----------------------------------
  1268.         LOCAL nPos,cSaveExact
  1269.         
  1270.         *- First check to see if we have 3.0 drivers
  1271.         FOR nPOS = 1 TO ALEN(THIS.aODBCDrivers,1)
  1272.             IF ATC(FOXODBC_30, THIS.aODBCDrivers[m.nPos, 1]) # 0
  1273.                 THIS.cODBC_DSN = THIS.aODBCDrivers[m.nPos, 1]
  1274.                 THIS.lHas30Drivers = .T.
  1275.                 RETURN
  1276.             ENDIF
  1277.         ENDFOR
  1278.         
  1279.         *- Check to see if we have "FoxPro Files" generic 2.6 driver
  1280.         cSaveExact = SET("EXACT")
  1281.         SET EXACT ON
  1282.         nPOS = ASCAN(THIS.aODBCDrivers, THIS.cODBC_DSN)
  1283.         IF m.nPOS == 0 OR nPos = ALEN(THIS.aODBCDrivers)
  1284.             nPOS = 1
  1285.             THIS.cODBC_DSN = THIS.aODBCDrivers[1, 1]
  1286.         ENDIF
  1287.         SET EXACT &cSaveExact
  1288.     ENDPROC
  1289.  
  1290.  
  1291.     *----------------------------------
  1292.     PROCEDURE CheckODBCFoxVer
  1293.     *----------------------------------
  1294.         *- This checks for the Win32s (16bit) DriverID ODBC.INI
  1295.         *- setting for FoxPro Files 2.6 section. It merely alerts user if there
  1296.         *- is an old driver installed.
  1297.  
  1298.         IF THIS.lHas30Drivers
  1299.             RETURN
  1300.         ENDIF    
  1301.  
  1302.         LOCAL aFoxSect,retlen,i,cValue
  1303.         DIMENSION aFoxSect[1]
  1304.         IF THIS.nCurrentOS = OS_W32S    && Win322
  1305.             IF oreg.GetINISection(@aFoxSect,THIS.aODBCDrivers[1,1],ODBC_FILE)#0
  1306.                 *- Failed to read INI file, so skip this check
  1307.                 RETURN
  1308.             ENDIF
  1309.             FOR i = 1 TO ALEN(aFoxSect)
  1310.                 IF ATC(C_DRIVEID,aFoxSect[m.i])#0    && Look for DriverID
  1311.                     cValue = ""            
  1312.                     oreg.GetINIEntry(@cValue,THIS.aODBCDrivers[1,1],aFoxSect[m.i],ODBC_FILE)
  1313.                     IF VAL(m.cValue) = FOX_DRIVEID    &&24
  1314.                         THIS.ALERT(C_ODBCOLDVER_LOC)
  1315.                     ENDIF
  1316.                     EXIT
  1317.                 ENDIF
  1318.             ENDFOR
  1319.         ELSE
  1320.             *- Check for correct version in NT, Win95
  1321.             IF oReg.EnumOptions(@aFoxSect,ODBC_DATA_KEY+THIS.aODBCDrivers[1,1],HKEY_CURRENT_USER,.F.)#0
  1322.                 *- Failed to read Registry, so skip this check
  1323.                 RETURN
  1324.             ENDIF
  1325.             FOR i = 1 TO ALEN(aFoxSect,1)
  1326.                 IF ATC(C_FIL,aFoxSect[m.i,1])#0    AND ATC(C_FOX2,aFoxSect[m.i,2]) # 0        && Look for FIL
  1327.                     THIS.ALERT(C_ODBCOLDVER_LOC)
  1328.                     EXIT
  1329.                 ENDIF
  1330.                 IF ATC(C_DRIVEID,aFoxSect[m.i,1])#0    &&Look for DriverID
  1331.                     *- test for DWORD first and then ASCII
  1332.                     IF (ASC(SUBSTR(aFoxSect[m.i,2],1,1)) = FOX_DRIVEID AND ASC(SUBSTR(aFoxSect[m.i,2],2,1))=0) OR;
  1333.                         VAL(aFoxSect[m.i,2]) = FOX_DRIVEID
  1334.                         THIS.ALERT(C_ODBCOLDVER_LOC)
  1335.                         EXIT
  1336.                     ENDIF
  1337.                 ENDIF
  1338.             ENDFOR
  1339.         ENDIF
  1340.     ENDPROC
  1341.  
  1342.  
  1343.  
  1344.     *----------------------------------
  1345.     FUNCTION MailMergeMacWord6
  1346.     *----------------------------------
  1347.         *- drive MS Word for Macintosh 6.0
  1348.         *-
  1349.         *- step 1: Create text file of data
  1350.         *- step 2: Create Applescript using textmerge that will open specified
  1351.         *-         file, and attach data to it.
  1352.         *- step 3: Run Applescript
  1353.     
  1354.         LOCAL cscript, lscripterr
  1355.  
  1356.         IF !_mac
  1357.             *- this is only for the Mac
  1358.             RETURN .F.
  1359.         ENDIF
  1360.  
  1361.         IF EMPTY(THIS.cexe)
  1362.             THIS.cexe = ""
  1363.             *- locate MS Word 6.0
  1364.             *- look to see if there is a "Word Settings (6)" file in
  1365.             *- the Preferences folder
  1366.             *- check the resource file
  1367.             IF !FILE(SYS(2033,2) + ":" + C_WORDSETTINGS)
  1368.                 *- assume word is not installed
  1369.                 THIS.Alert(E_NOWORDMACERR_LOC)
  1370.                 RETURN .F.
  1371.             ENDIF
  1372.             THIS.cexe = THIS.GetPref(C_MAILMRG_SECT,C_WORD6ID,C_FOXPROINI_MAC)
  1373.             IF !FILE(THIS.cexe)
  1374.                 THIS.cexe = ""
  1375.             ENDIF
  1376.             IF EMPTY(THIS.cexe)
  1377.                 THIS.cexe = SYS(2027,GETFILE("",C_LOCATE_LOC + C_MSWORDMAC,"",0,'APPL'))
  1378.                 IF EMPTY(THIS.cexe)
  1379.                     *- cancelled
  1380.                     RETURN .F.
  1381.                 ELSE
  1382.                     *- update prefs
  1383.                     =THIS.PutPref(C_MAILMRG_SECT,C_WORD6ID,THIS.cexe,C_FOXPROINI_MAC)
  1384.                 ENDIF
  1385.             ENDIF
  1386.         ENDIF
  1387.  
  1388.         *- works the same as Word for DOS, except will go on
  1389.         *- to pass name of text data file to Word via DDE
  1390.         IF !THIS.MrgCommaDel(',')
  1391.             RETURN .F.
  1392.         ENDIF
  1393.  
  1394.  
  1395.         WAIT WINDOW NOWAIT C_STARTWORD60_LOC
  1396.  
  1397.         *- prepare to create Applescript
  1398.         cscript = SYS(2027,SYS(2023)) + SYS(3) + ".script"
  1399.  
  1400.         SET TEXTMERGE TO (m.cscript)
  1401.         SET TEXTMERGE ON NOSHOW
  1402.         \\ -- AppleScript<<CHR(170)>> script to drive MS Word for Macintosh 6.0
  1403.         \tell application "<<THIS.cExe>>"
  1404.         *- bring to front
  1405.         \    Activate
  1406.         *- open document, or create a new one
  1407.         IF THIS.nNewDoc = N_EXISTING_DOC
  1408.             \    Open "<<THIS.cDocName>>"
  1409.             \    do script <<CHR(194)>>
  1410.             \        "MailMergeOpenDataSource .Name = \"<<SYS(2027,THIS.cSaveFile)>>\"
  1411.         ELSE
  1412.             \    do script <<CHR(194)>>
  1413.             \        "
  1414.             \\FileNew
  1415.             DO CASE
  1416.                 CASE THIS.nTemplate = N_LABEL
  1417.                     \        MailMergeMainDocumentType 1
  1418.                 CASE THIS.nTemplate = N_ENVELOPE
  1419.                     \        MailMergeMainDocumentType 2
  1420.                 CASE THIS.nTemplate = N_CATALOG
  1421.                     \        MailMergeMainDocumentType 3
  1422.             ENDCASE
  1423.             *- attach data file
  1424.             \        MailMergeOpenDataSource .Name = \"<<SYS(2027,THIS.cSaveFile)>>\"
  1425.         ENDIF
  1426.  
  1427.  
  1428. #IF 0
  1429.     *- the next few lines show how to use ODBC
  1430.     *- in this version we generate a text file of data instead, and attach it to a Word
  1431.     *- doc for the user (see above)
  1432.         \        MailMergeOpenDataSource .Name = \"<<SYS(2027,THIS.cDataSrc)>>\",
  1433.         \\ .Connection = \"DSN=<<THIS.cODBC_DSN>>;DBQ=<<SYS(2027,SET("DEFA") + SYS(2003))>>;FIL=<<THIS.cODBC_FIL>>\", 
  1434.         \\ .SQLStatement = \"<<THIS.csqlstmt>>\"
  1435. #ENDIF
  1436.  
  1437.         IF THIS.nNewDoc = N_NEW_DOC AND (THIS.nTemplate = N_LABEL OR THIS.nTemplate = N_ENVELOPE)
  1438.             \        Dim dlg As MailMergeHelper
  1439.             \        GetCurValues dlg
  1440.             \        x = Dialog(dlg)
  1441.         ENDIF
  1442.         *- end WordBasic script
  1443.         \        "
  1444.         \end tell
  1445.         \
  1446.  
  1447.         *- close textmerge file, strip out linefeeds
  1448.         SET TEXTMERGE TO
  1449.         THIS.FxStripLF(m.cscript)
  1450.  
  1451.         THIS.SetErrorOff = .T.
  1452.  
  1453.         *- now go ahead and run it
  1454.         RUNSCRIPT (m.cscript)
  1455.  
  1456.         *- restore normal error handling
  1457.         THIS.SetErrorOff = .F.
  1458.  
  1459. #IF 0
  1460.         *- second script, to display appropriate dialog, if labels or envelopes
  1461.         IF THIS.nNewDoc = N_NEW_DOC AND (THIS.nTemplate = N_LABEL OR THIS.nTemplate = N_ENVELOPE)
  1462.             cscript2 = SYS(2027,SYS(2023)) + SYS(3) + ".script"
  1463.  
  1464.             SET TEXTMERGE TO (m.cscript2)
  1465.             SET TEXTMERGE ON NOSHOW
  1466.             \\ -- AppleScript<<CHR(170)>> script to drive MS Word for Macintosh 6.0
  1467.             \tell application "<<THIS.cExe>>"
  1468.             \    with timeout of 0 seconds
  1469.             \        do script <<CHR(194)>>
  1470.             \            "Dim dlg As MailMergeHelper
  1471.             \            GetCurValues dlg
  1472.             \            x = Dialog(dlg)"
  1473.             \    end timeout
  1474.             \end tell
  1475.             \
  1476.  
  1477.             *- close textmerge file, strip out linefeeds
  1478.             SET TEXTMERGE TO
  1479.             THIS.FxStripLF(m.cscript2)
  1480.  
  1481.             THIS.SetErrorOff = .T.
  1482.  
  1483.             *- now go ahead and run it
  1484.             RUNSCRIPT (m.cscript2)
  1485.  
  1486.             *- restore normal error handling
  1487.             THIS.SetErrorOff = .F.
  1488.  
  1489.             *- toss the script file 2
  1490.             ERASE (m.cscript2)
  1491.  
  1492.         ENDIF
  1493. #ENDIF
  1494.         IF !L_DEBUG
  1495.             *- toss the script file 1
  1496.             ERASE (m.cscript)
  1497.         ENDIF
  1498.  
  1499.         WAIT CLEAR
  1500.  
  1501.         RETURN .T.
  1502.  
  1503.     ENDFUNC
  1504.  
  1505.  
  1506.     *----------------------------------
  1507.     FUNCTION  mswerr
  1508.     *----------------------------------
  1509.         *- Error handler while script is being run
  1510.         PARAMETER errnum, cmsg
  1511.  
  1512.         #DEFINE N_RUNSCRIPTFAIL        1921    && FP error numbers
  1513.         #DEFINE N_SCRIPTERROR        1917
  1514.  
  1515.         IF errnum = K_RUNSCRIPTFAIL
  1516.             THIS.MrgCommaDel
  1517.             THIS.ALERT(E_NOAPPLESCRIPT_LOC)
  1518.         ENDIF
  1519.  
  1520.         RETURN .T.
  1521.  
  1522.     ENDFUNC
  1523.  
  1524.  
  1525.  
  1526. ENDDEFINE    && MailMerge
  1527.  
  1528. *- eof